#!/usr/bin/env python
# -*- coding: utf-8 -*-

import os
import hashlib
import shutil
import sys
import tkinter as tk
from tkinter import messagebox, filedialog
import difflib
import filecmp

def calculate_file_hash(file_path):
    """
    计算文件的MD5哈希值
    :param file_path: 文件路径
    :return: 文件的MD5哈希值
    """
    hasher = hashlib.md5()
    with open(file_path, 'rb') as file:
        # 读取文件内容并更新哈希对象
        # 分块读取文件以处理大文件
        buf = file.read(65536)
        while len(buf) > 0:
            hasher.update(buf)
            buf = file.read(65536)
    return hasher.hexdigest()

def compare_files_content(file1, file2, max_lines=10):
    """
    比较两个文件的内容
    :param file1: 第一个文件的路径
    :param file2: 第二个文件的路径
    :param max_lines: 最大比较行数
    :return: 是否完全相同, 文件1预览, 文件2预览, 差异信息
    """
    # 首先使用filecmp执行严格比较
    if not filecmp.cmp(file1, file2, shallow=False):
        return False, None, None, "文件内容不同"
    
    # 对于文本文件，提供内容预览
    try:
        # 尝试读取文本文件内容
        with open(file1, 'r', encoding='utf-8', errors='replace') as f1:
            content1 = f1.readlines()[:max_lines]
        
        with open(file2, 'r', encoding='utf-8', errors='replace') as f2:
            content2 = f2.readlines()[:max_lines]
        
        # 计算差异
        diff = list(difflib.unified_diff(content1, content2, n=0))
        
        # 如果有差异，显示差异
        if diff:
            return False, content1, content2, "".join(diff)
        
        return True, content1, content2, "文件内容完全相同"
    except UnicodeDecodeError:
        # 非文本文件，二进制比较
        return True, None, None, "二进制文件内容相同，无法显示预览"
    except Exception as e:
        return True, None, None, f"比较过程发生错误: {e}"

def find_duplicate_files(directory, verify_content=True):
    """
    查找目录下的重复文件
    :param directory: 要扫描的目录路径
    :param verify_content: 是否通过内容验证确认文件相同
    :return: 包含重复文件信息的字典，键为文件哈希值，值为文件路径列表
    """
    # 初始化存储文件哈希值的字典
    file_hashes = {}
    
    # 遍历目录下的所有文件
    for root, _, files in os.walk(directory):
        for filename in files:
            # 构建完整的文件路径
            file_path = os.path.join(root, filename)
            
            # 排除隐藏文件和可能的输出目录中的文件
            if os.path.basename(root) == "output" or os.path.basename(filename).startswith('.'):
                continue
                
            try:
                # 计算文件哈希值
                file_hash = calculate_file_hash(file_path)
                
                # 将文件路径添加到对应哈希值的列表中
                if file_hash in file_hashes:
                    file_hashes[file_hash].append(file_path)
                else:
                    file_hashes[file_hash] = [file_path]
            except Exception as e:
                print(f"处理文件 {file_path} 时出错: {e}")
    
    # 筛选出重复的文件（哈希值相同的多个文件）
    duplicate_files = {hash_val: paths for hash_val, paths in file_hashes.items() if len(paths) > 1}
    
    # 如果需要验证，进一步验证文件内容
    if verify_content and duplicate_files:
        print("正在验证文件内容...")
        verified_duplicates = {}
        
        for hash_val, file_paths in duplicate_files.items():
            verified_paths = []
            # 以第一个文件为基准
            base_file = file_paths[0]
            verified_paths.append(base_file)
            
            # 与其他文件比较
            for file_path in file_paths[1:]:
                identical, _, _, _ = compare_files_content(base_file, file_path)
                if identical:
                    verified_paths.append(file_path)
            
            # 只有至少有两个验证过的相同文件才添加到结果中
            if len(verified_paths) > 1:
                verified_duplicates[hash_val] = verified_paths
        
        return verified_duplicates
    
    return duplicate_files

def copy_duplicates_to_output(duplicates, base_dir):
    """
    将重复文件复制到output目录
    :param duplicates: 包含重复文件信息的字典
    :param base_dir: 基础目录路径，用于创建output目录
    :return: 复制的文件数量
    """
    # 创建output目录
    output_dir = os.path.join(base_dir, "output")
    os.makedirs(output_dir, exist_ok=True)
    
    copied_count = 0
    
    # 为每组重复文件创建一个子目录
    for hash_val, file_paths in duplicates.items():
        # 创建以哈希值命名的子目录
        duplicate_dir = os.path.join(output_dir, f"duplicate_{hash_val[:8]}")
        os.makedirs(duplicate_dir, exist_ok=True)
        
        # 复制所有重复文件到子目录
        for file_path in file_paths:
            # 使用原始文件名，但确保不会有命名冲突
            filename = os.path.basename(file_path)
            dest_path = os.path.join(duplicate_dir, filename)
            
            # 如果已存在同名文件，添加序号
            counter = 1
            while os.path.exists(dest_path):
                name, ext = os.path.splitext(filename)
                dest_path = os.path.join(duplicate_dir, f"{name}_{counter}{ext}")
                counter += 1
            
            # 复制文件
            try:
                shutil.copy2(file_path, dest_path)
                copied_count += 1
            except Exception as e:
                print(f"复制文件 {file_path} 时出错: {e}")
    
    return copied_count

def create_report(duplicates, output_dir):
    """
    创建重复文件报告
    :param duplicates: 包含重复文件信息的字典
    :param output_dir: 输出目录路径
    """
    report_path = os.path.join(output_dir, "duplicate_files_report.txt")
    
    with open(report_path, 'w', encoding='utf-8') as report_file:
        report_file.write("重复文件报告\n")
        report_file.write("=" * 50 + "\n\n")
        
        if not duplicates:
            report_file.write("未发现重复文件。\n")
            return
        
        total_groups = len(duplicates)
        total_files = sum(len(paths) for paths in duplicates.values())
        
        report_file.write(f"发现 {total_groups} 组重复文件，共 {total_files} 个文件。\n\n")
        
        for i, (hash_val, file_paths) in enumerate(duplicates.items(), 1):
            report_file.write(f"重复组 {i}:\n")
            report_file.write(f"哈希值: {hash_val}\n")
            report_file.write(f"文件数: {len(file_paths)}\n")
            report_file.write("文件列表:\n")
            
            for path in file_paths:
                report_file.write(f"  - {path}\n")
            
            # 添加文件内容比较结果
            if len(file_paths) >= 2:
                report_file.write("\n内容比较结果:\n")
                base_file = file_paths[0]
                
                # 尝试读取并展示基准文件的前几行
                try:
                    with open(base_file, 'r', encoding='utf-8', errors='replace') as f:
                        base_content = f.readlines()[:5]  # 只显示前5行
                    report_file.write(f"基准文件 ({base_file}) 内容预览:\n")
                    for line in base_content:
                        report_file.write(f"    {line}")
                    report_file.write("\n")
                except:
                    report_file.write(f"基准文件 ({base_file}) 无法显示内容预览（可能是二进制文件）\n\n")
                
                # 与其他文件比较
                for compare_file in file_paths[1:]:
                    identical, _, _, diff_message = compare_files_content(base_file, compare_file)
                    report_file.write(f"与文件 ({compare_file}) 比较结果: {'内容完全相同' if identical else '存在差异'}\n")
                    if not identical and diff_message and len(diff_message) < 500:  # 只展示较短的差异信息
                        report_file.write("差异信息:\n")
                        report_file.write(diff_message)
                        report_file.write("\n")
            
            report_file.write("\n")

def create_detailed_content_report(duplicates, output_dir):
    """
    创建详细的文件内容比较报告
    :param duplicates: 包含重复文件信息的字典
    :param output_dir: 输出目录路径
    """
    detail_report_path = os.path.join(output_dir, "content_comparison_details.txt")
    
    with open(detail_report_path, 'w', encoding='utf-8') as report_file:
        report_file.write("文件内容详细比较报告\n")
        report_file.write("=" * 60 + "\n\n")
        
        for group_id, (hash_val, file_paths) in enumerate(duplicates.items(), 1):
            report_file.write(f"重复组 {group_id} (哈希值: {hash_val})\n")
            report_file.write("-" * 60 + "\n\n")
            
            # 以第一个文件为基准
            base_file = file_paths[0]
            report_file.write(f"基准文件: {base_file}\n\n")
            
            # 比较其他文件与基准文件
            for file_index, compare_file in enumerate(file_paths[1:], 1):
                report_file.write(f"比较文件 {file_index}: {compare_file}\n")
                
                # 获取文件比较结果
                identical, content1, content2, diff_info = compare_files_content(
                    base_file, compare_file, max_lines=20
                )
                
                report_file.write(f"比较结果: {'内容完全相同' if identical else '存在差异'}\n")
                
                # 处理文本文件
                if content1 is not None and content2 is not None:
                    report_file.write("\n基准文件内容预览:\n")
                    report_file.write("".join(content1) or "(空文件)\n")
                    
                    report_file.write("\n比较文件内容预览:\n")
                    report_file.write("".join(content2) or "(空文件)\n")
                    
                    report_file.write("\n差异信息:\n")
                    report_file.write(diff_info or "无差异\n")
                else:
                    report_file.write("\n(二进制文件或无法读取的文件)\n")
                
                report_file.write("-" * 40 + "\n\n")
            
            report_file.write("=" * 60 + "\n\n")

def select_directory():
    """
    打开文件对话框让用户选择目录
    :return: 用户选择的目录路径
    """
    root = tk.Tk()
    root.withdraw()  # 隐藏主窗口
    
    # 显示目录选择对话框
    directory = filedialog.askdirectory(title="选择要扫描的文件夹")
    
    if not directory:  # 用户取消了选择
        return None
    
    return directory

def main():
    """
    主函数，处理命令行参数并执行查找重复文件的功能
    """
    print("文件重复查找工具启动...")
    
    # 如果没有提供命令行参数，打开目录选择对话框
    if len(sys.argv) > 1:
        directory = sys.argv[1]
    else:
        directory = select_directory()
    
    if not directory:
        print("未选择目录，程序退出。")
        input("按Enter键退出...")
        return
    
    print(f"正在扫描目录: {directory}")
    
    # 查找重复文件
    duplicates = find_duplicate_files(directory, verify_content=True)
    
    if not duplicates:
        print("未发现重复文件。")
        messagebox.showinfo("查找完成", "未发现重复文件。")
        input("按Enter键退出...")
        return
    
    # 统计重复文件信息
    total_groups = len(duplicates)
    total_files = sum(len(paths) for paths in duplicates.values())
    
    print(f"发现 {total_groups} 组重复文件，共 {total_files} 个文件。")
    
    # 创建output目录并复制重复文件
    copied_count = copy_duplicates_to_output(duplicates, directory)
    
    # 创建报告
    output_dir = os.path.join(directory, "output")
    create_report(duplicates, output_dir)
    
    # 创建详细内容比较报告
    create_detailed_content_report(duplicates, output_dir)
    
    print(f"已将 {copied_count} 个重复文件复制到 {output_dir}")
    print(f"基本报告已保存到 {os.path.join(output_dir, 'duplicate_files_report.txt')}")
    print(f"详细内容比较报告已保存到 {os.path.join(output_dir, 'content_comparison_details.txt')}")
    
    messagebox.showinfo("查找完成", 
                       f"发现 {total_groups} 组重复文件，共 {total_files} 个文件。\n"
                       f"已将重复文件复制到 {output_dir}\n"
                       f"基本报告和详细内容比较报告已保存。")
    
    input("按Enter键退出...")

if __name__ == "__main__":
    try:
        main()
    except Exception as e:
        print(f"程序遇到错误: {e}")
        messagebox.showerror("错误", f"程序遇到错误: {e}")
        input("按Enter键退出...") 